﻿using Longbow.OAuth;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace Longbow.WeChatAuth
{
    internal class WeChatHandler : LgbOAuthHandler<WeChatOptions>
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        /// <param name="options"></param>
        /// <param name="logger"></param>
        /// <param name="encoder"></param>
        /// <param name="clock"></param>
        public WeChatHandler(IOptionsMonitor<WeChatOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
        {

        }

        /// <summary>
        /// 生成认证地址方法
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="redirectUri"></param>
        /// <returns></returns>
        protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
        {
            var scopeParameter = properties.GetParameter<ICollection<string>>(OAuthChallengeProperties.ScopeKey);
            var scope = scopeParameter != null ? FormatScope(scopeParameter) : FormatScope();

            var parameters = new Dictionary<string, string>()
            {
                { "appid", Options.ClientId },
                { "redirect_uri", redirectUri },
                { "scope", scope },
                { "response_type", "code" }
            };
            parameters["state"] = Options.StateDataFormat.Protect(properties);
            return QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, parameters);
        }

        /// <summary>
        /// 通过 code 获取 access_token 方法
        /// </summary>
        /// <param name="code"></param>
        /// <param name="redirectUri"></param>
        /// <remarks>标准的都是 post 方法 微信使用 get</remarks>
        protected override async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri)
        {
            var parameters = new Dictionary<string, string>
            {
                {  "appid", Options.ClientId },
                {  "secret", Options.ClientSecret },
                {  "code", code},
                {  "grant_type", "authorization_code" }
            };

            var endpoint = QueryHelpers.AddQueryString(Options.TokenEndpoint, parameters);
            var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted);
            if (response.IsSuccessStatusCode)
            {
                var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
                return OAuthTokenResponse.Success(payload);
            }
            else
            {
                var error = "OAuth token endpoint failure: " + await Display(response);
                return OAuthTokenResponse.Failed(new Exception(error));
            }
        }

        /// <summary>
        /// 生成用户信息请求地址方法
        /// </summary>
        /// <param name="tokens"></param>
        /// <returns></returns>
        protected override string BuildUserInfoUrl(OAuthTokenResponse tokens)
        {
            var tokenRequestParameters = new Dictionary<string, string>()
            {
                { "access_token", tokens.AccessToken },
                { "openid", tokens.Response.Value<string>("openid") }
            };
            return QueryHelpers.AddQueryString(Options.UserInformationEndpoint, tokenRequestParameters);
        }

        /// <summary>
        /// Format a list of OAuth scopes.
        /// </summary>
        /// <param name="scopes">List of scopes.</param>
        /// <returns>Formatted scopes.</returns>
        protected override string FormatScope(IEnumerable<string> scopes) => string.Join(",", scopes); // 微信 scope 使用逗号分隔
    }
}
